#!/bin/sh

MOD_NAME=eth1x_auth
MOD_DEFAULTS=/opt/$MOD_NAME/etc/defaults
MOD_SETTINGS=/opt/$MOD_NAME/etc/settings
MODCHAIN="mod_$MOD_NAME"

HOSTAPD_NAME=hostapd.1x
HOSTAPD_BIN=/opt/$MOD_NAME/bin/$HOSTAPD_NAME
HOSTAPD_CONF=/var/$MOD_NAME/hostapd.conf

HOSTAPD_CLI_NAME=hostapd_cli.1x
HOSTAPD_CLI_BIN=/opt/$MOD_NAME/bin/$HOSTAPD_CLI_NAME

IPT_LIST="/sbin/iptables /sbin/ip6tables"
IPT_CONTROLLER=/opt/$MOD_NAME/bin/ipt_controller

[ -f "$MOD_SETTINGS" ] || cp $MOD_DEFAULTS $MOD_SETTINGS
. $MOD_SETTINGS

ALL_IFACES="eth0 eth1 eth2 br0"
# build a list of enabled interfaces
if [ "$MOD_ETH1X_AUTH_ON_ETH0" = "1" ]; then ifconfig eth0 >/dev/null && IFACES="$IFACES eth0"; fi
if [ "$MOD_ETH1X_AUTH_ON_ETH1" = "1" ]; then ifconfig eth1 >/dev/null && IFACES="$IFACES eth1"; fi
if [ "$MOD_ETH1X_AUTH_ON_ETH2" = "1" ]; then ifconfig eth2 >/dev/null && IFACES="$IFACES eth2"; fi
if [ "$MOD_ETH1X_AUTH_ON_BR0"  = "1" ]; then ifconfig br0 >/dev/null && IFACES="$IFACES br0";  fi

start_ifaces() {
  # create the hostapd.conf file
  mkdir -p /var/$MOD_NAME
  echo "ctrl_interface=/var/run/hostapd" > $HOSTAPD_CONF
  echo "driver=wired" >> $HOSTAPD_CONF
  echo "ieee8021x=1" >> $HOSTAPD_CONF
  echo "eap_reauth_period=$MOD_ETH1X_AUTH_REAUTH_PERIOD" >> $HOSTAPD_CONF
  echo "use_pae_group_addr=1" >> $HOSTAPD_CONF

  echo "auth_server_addr=$MOD_ETH1X_AUTH_RADIUS_AUTH0_ADDR" >> $HOSTAPD_CONF
  echo "auth_server_shared_secret=$MOD_ETH1X_AUTH_RADIUS_AUTH0_PASS" >> $HOSTAPD_CONF
  if [ ! -z "$MOD_ETH1X_AUTH_RADIUS_AUTH0_PORT" ]; then
    echo "auth_server_port=$MOD_ETH1X_AUTH_RADIUS_AUTH0_PORT" >> $HOSTAPD_CONF
  fi

  if [ ! -z "$MOD_ETH1X_AUTH_RADIUS_ACCT0_ADDR" ]; then
    echo "acct_server_addr=$MOD_ETH1X_AUTH_RADIUS_ACCT0_ADDR" >> $HOSTAPD_CONF
    echo "acct_server_shared_secret=$MOD_ETH1X_AUTH_RADIUS_ACCT0_PASS" >> $HOSTAPD_CONF
    if [ ! -z "$MOD_ETH1X_AUTH_RADIUS_ACCT0_PORT" ]; then
      echo "acct_server_port=$MOD_ETH1X_AUTH_RADIUS_ACCT0_PORT" >> $HOSTAPD_CONF
    fi
  fi

  case $MOD_ETH1X_AUTH_SYSLOGLEV in
    0) SYSLOG_OPT="-s -dd -K" ;;
    1) SYSLOG_OPT="-s -d" ;;
    2) SYSLOG_OPT="-s" ;;
    *) SYSLOG_OPT="" ;;
  esac

  # start the hostapd process
  # it seems the hostapd cannot handle multiple interfaces by one process
  for IFN in $IFACES; do
    $HOSTAPD_BIN $SYSLOG_OPT -B -i $IFN $HOSTAPD_CONF || return $?
    $HOSTAPD_CLI_BIN -B -i $IFN -a $IPT_CONTROLLER || return $?
  done
}

init_chain() {
  for IT in $IPT_LIST; do
    [ -x $IT ] || continue
    IPT="$IT -t mangle"
    # single chain for all authorized interfaces
    $IPT -N $MODCHAIN || continue
    # add exempted MAC addresses
    I=0
    while [ $I -le 16 ]; do
      I=$((I+1))
      eval MACADDR='$'MOD_ETH1X_AUTH_EXE${I}_MACADDR
      if [ ! -z $MACADDR ]; then
        # always use semicolons
        $IPT -I $MODCHAIN -m mac --mac-source ${MACADDR//-/:} -j RETURN
      fi
    done
    # as a last step, if not authorized nor exempted, block all traffic
    # note iptables do not see (cannot block) non IP traffic such as ARP or EAPoL
    $IPT -A $MODCHAIN -j DROP

    for IFN in $IFACES; do
      $IPT -I PREROUTING -i $IFN -j $MODCHAIN
    done
  done
}

del_chain() {
  for IT in $IPT_LIST; do
    [ -x $IT ] || continue
    IPT="$IT -t mangle"
    # we don't know what interfaces were enabled, so we try to delete all
    for IFN in $ALL_IFACES; do
      $IPT -D PREROUTING -i $IFN -j $MODCHAIN
    done
    $IPT -F $MODCHAIN
    $IPT -X $MODCHAIN
  done
}

terminate() {
  killall $HOSTAPD_NAME 2> /dev/null
  killall $HOSTAPD_CLI_NAME 2> /dev/null
  # remove hostapd configuration
  rm -rf /var/$MOD_NAME

  del_chain 2>/dev/null
}

case "$1" in
  start|restart)
    if [ "$1" = "restart" ]; then
      echo -n "Restarting module $MOD_NAME: "
      terminate
    else
      echo -n "Starting module $MOD_NAME: "
    fi
    if [ "$MOD_ETH1X_AUTH_ENABLED" != "1" ]; then
      echo "skipped"
      exit 0
    fi

    start_ifaces
    RETVAL=$?
    if [ $RETVAL != 0 ]; then
      echo "failed"
      # undo what has been done so far
      killall $HOSTAPD_CLI_NAME $HOSTAPD_NAME 2> /dev/null
      rm -rf /var/$MOD_NAME
      exit $RETVAL
    fi

    init_chain 2>/dev/null
    RETVAL=$?
    if [ $RETVAL != 0 ]; then
      echo "failed"
      terminate
      exit $RETVAL
    else
      echo "done"
      exit 0
    fi
    ;;
  stop)
    echo -n "Stopping module $MOD_NAME: "
    terminate
    RETVAL=$?
    if [ $RETVAL = 0 ]; then echo "done"; else echo "failed"; fi
    exit $RETVAL
    ;;
  status)
    if [ "$MOD_ETH1X_AUTH_ENABLED" != "1" ]; then
      echo "Disabled"
      exit 0
    fi
    echo "-------------------"
    for IFN in $IFACES; do
      echo "$IFN authenticated:"
      $HOSTAPD_CLI_BIN -i $IFN list_sta
      echo
      echo "-------------------"
    done
    ;;
  defaults)
    cp $MOD_DEFAULTS $MOD_SETTINGS 2> /dev/null
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|status|defaults}"
    exit 1
esac
